import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;

/*Class used to run the tests needed for the report*/

public class ClientTests{

	public Socket clientSocket;
	public DataOutputStream outToServer;
	public BufferedReader inFromServer;
	public ArrayBlockingQueue<String> queue;
	public ClientThread cthread;
	
	
	public static void main(String[] args) {
		ClientTests c = new ClientTests();
	}

	public ClientTests() {

		//initialize
		queue = new ArrayBlockingQueue<String>(Global.queueSize); //create server for client to receive responses from
		cthread = new ClientThread(queue);
		cthread.start();
		
		//pick a random server and initialize
		ArrayList<MachineData> machines = MachineData.getList(Global.filename);
		int N = machines.size();
		MachineData m = machines.get((int) (Math.random()*N));

		try {
			clientSocket = new Socket(m.ip, m.port);
			outToServer = new DataOutputStream(clientSocket.getOutputStream());
			inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		insertTest(m);
		System.out.println("DONE");
	}
	
	public void insertTest(MachineData m) { //test speed of inserts
		//pick a key and value
		int runs = 1000;
		double[] seconds = new double[runs];
				

		for(int i=0; i < runs; i++) {
		//send insert request
			int key = (int) (Math.random()*1000000) + 1;
			String value = (new Integer(key)).toString();
			long start = System.currentTimeMillis();
			insert(key,value,m);
			long end = System.currentTimeMillis();
			delete(key, m);
			seconds[i] = (end - start) /1000.;
		}
		
		for(int i = 0; i < runs; i++) {
			System.out.println(seconds[i]);
		}
		
		//shutdown
		cthread.flag = false;
		
		try {
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.exit(0);
	}

	public void getTest(MachineData m) { //test speed of lookups
		//pick a key and value
		double[] seconds = new double[1000];
		
		for(int i=0; i < 1000; i++) {
		//send insert request
			int key = (int) (Math.random()*1000000) + 1;
			String value = (new Integer(key)).toString();
			long start = System.currentTimeMillis();
			get(key, m);
			long end = System.currentTimeMillis();
			seconds[i] = (end - start) /1000.;
		}
		
		for(int i = 0; i < 1000; i++) {
			System.out.println(seconds[i]);
		}
		
		//shutdown
		cthread.flag = false;
		
		try {
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.exit(0);
	}
	
	public void loadTest(MachineData m) { //test distribution of keys
		//pick a key and value
		
		for(int i=0; i < 1000; i++) {
		//send insert request
			int key = (int) (Math.random()*1000000) + 1;
			String value = (new Integer(key)).toString();
			insert(key, value, m);
		}

		//shutdown
		cthread.flag = false;
		
		try {
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.exit(0);
	}
	
	public void messageTest(MachineData m) { //test number of forwarded messages
		//pick a key and value
		
		for(int i=0; i < 1000; i++) {
		//send insert request
			int key = (int) (Math.random()*1000000) + 1;
			String value = (new Integer(key)).toString();
			get(key, m);
			try {
				Thread.sleep(250);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		//shutdown
		cthread.flag = false;
		
		try {
			clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.exit(0);
	}
	
	public String get(int key, MachineData m) {
		//send request
		String message = "GET " + key;
		sendMessage(message, m);
		System.out.println(message + " " + m.port);
		
		//get confirmation
		String c = getConfirmation();
		System.out.println(c);

		return c;
	}

	public String insert(int key, String value, MachineData m) {
		//send request		
		String message = "INSERT " + key + " " + value;
		System.out.println(message + " " + m.port);
		//System.out.println(message + " " + m.port);
		
		sendMessage(message, m);

		//get confirmation
		String c = getConfirmation();
		System.out.println(c);

		return c;
	}

	public String delete(int key, MachineData m) {
		//send request	
		String message = "DELETE " + key;
		sendMessage(message, m);
		System.out.println(message + " " + m.port);
		
		//get confirmation
		String c = getConfirmation();
		System.out.println(c);

		return c;
	}

	public void sendMessage(String message, MachineData m) {
		try {
			outToServer.writeBytes(message + '\n');
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public String getConfirmation() { //wait until server responds

		while(queue.isEmpty()) {
		}
		
		try {
			return queue.take();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		return null;
	}

	//server of client that accepts responses from server and puts them in blocking queue
	private class ClientThread extends Thread {

		public ArrayBlockingQueue<String> queue;
		public ServerSocket socket;
		public boolean flag = true;
		
		public ClientThread(ArrayBlockingQueue<String> queue) {
			this.queue = queue;
		}

		public void run() {

			try {
				socket = new ServerSocket(Global.clientPortr);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(0);
			}

			while(flag) {
				try {
					Socket s = socket.accept();

					PrintWriter out = new PrintWriter(s.getOutputStream(),
							true);
					BufferedReader in = new BufferedReader(new InputStreamReader(
							s.getInputStream()));

					String data = in.readLine();

					queue.add(data);
				} catch (IOException e) {
					e.printStackTrace();
					System.exit(0);
				}
			}
			
			try {
				socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
